import java.io.*;
import java.util.regex.*;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.*;

public class Parser
{

  /**
   * Converte arquivo XML criado mediante transformação de documento PDF baixado
   * do Portal da Transparência Campinas contendo dados da remuneração mensal de
   * servidores, visando importação dos dados para tabelas do SQLite.
   * Observação: Usa-se o aplicativo "pdftohtml" para conversão do PDF a XML.
   *
   * @param filename Nome do arquivo XML objeto da extração.
  */
  public void parse(String filename)
  {
    // separador default de campos de registro do SQLite
    final String separator = "|";

    final String encoding = "UTF8"; // encoding default

    // Qualified Name do atributo "top" de elemento "text"
    // identificador de campos de registro em extração
    final QName top = QName.valueOf("top");

    // matcher para testar representação de valor monetário inferior a um milhão
    // grafado no padrão do Brasil com captura de componentes conforme magnitude
    // nota: inicializado com valor dummy
    Matcher currency = Pattern.compile("(\\d{1,3})(?:\\.(\\d{3}))?,(\\d{2})").matcher("");

    // buffer dos campos de registros extraídos a cada iteração
    StringBuilder buffer = new StringBuilder();

    String prevId = null; // identificação de registro em extração
    int count = 0;        // contador de campos de registro em extração

    try {

      // prepara factory de engines de análise de arquivos XML
      // e requisita engine de análise por eventos
      XMLInputFactory factory = XMLInputFactory.newInstance();
      InputStream in = new FileInputStream(filename);
      XMLEventReader parser = factory.createXMLEventReader(in, encoding);

      // loop de extração dos registros
      while ( parser.hasNext() ) {
        XMLEvent event = parser.nextEvent();  // obtêm evento corrente
        if ( event.isStartElement() ) {
          // obtêm o elemento relacionado ao evento
          StartElement elm = event.asStartElement();
          // checa se o nome do elemento é "text"
          if ( elm.getName().toString().equals("text") ) {

            // obtêm o identificador de registro do elemento
            String id = elm.getAttributeByName(top).getValue();
            // checa se houve alteração do identificador de registro
            if ( ! id.equals(prevId) ) {
              // checa o número de campos p/qualificar registro de servidor
              if (count == 6 || count == 7) {
                // checa se o registro é incompleto
                if (count == 6) {
                  // insere separador após o segundo pré-existente
                  // para declarar o terceiro campo como nulo
                  buffer.insert(1+buffer.indexOf(separator, 1+buffer.indexOf(separator)), separator);
                }
                System.out.println(buffer.toString()); // descarga do registro
              }
              buffer.setLength(0);  // reinicia o buffer
              count = 0;
            }
            prevId = id;

            // pesquisa texto em possível nicho de elementos
            while ( ! parser.peek().isCharacters() ) parser.nextEvent();
            // obtêm valor textual de campo do registro
            String texto = parser.getElementText().trim();
            // adiciona separador de campos inter valores
            if (++count > 1) buffer.append(separator);
            // checa se o texto é representação de valor monetário
            if (count > 3 && currency.reset(texto).matches()) {
              // modifica texto para representar número real
              buffer.append(currency.group(1));
              if (currency.group(2) != null) buffer.append(currency.group(2));
              buffer.append('.').append(currency.group(3));
            } else {
              buffer.append(texto);
            }

          }
        }
      }

      parser.close(); // libera recursos de sistema

    } catch (IOException | XMLStreamException e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  public static void main(String[] args)
  {
    // redireciona a saída para arquivo cujo nome é o segundo argumento
    if (args.length == 2) try {
      System.setOut(
        new PrintStream(
          new BufferedOutputStream(
            new FileOutputStream(args[1]))));
    } catch (FileNotFoundException e) {
      e.printStackTrace();
      System.exit(1);
    }
    new Parser().parse(args[0]);
  }
}
